`timescale 1ns / 1ps

module pwc2_layer
#(
    parameter N_INH     = 4,
    parameter N_INW     = 4,
    parameter N_OUT     = 6,
    parameter N_ACT     = 2,
    parameter N_ICH     = 48,
    parameter N_OCH     = 96,
    parameter BIT_IN    = 8,
    parameter BIT_WT    = 5,
    parameter BIT_CV    = 16,
    parameter BIT_NB    = 16,
    parameter BIT_NM    = 16,
    parameter BIT_OT    = 8,
    // pwc weight ROM
    parameter PWCV_PE    = "DSP",
    parameter PWET_FILE  = "L3_PW.mem",
    parameter PWET_TYPE  = "block",
    parameter PWET_LATENCY = 2,
    // pwc buffer RAM
    parameter PBUF_TYPE  = "distributed",
    parameter PBUF_LATENCY = 1,
    // norm weight ROM
    parameter NORM_PE    = "DSP",
    parameter NORM_FILE  = "L3_BM.mem",
    parameter NORM_TYPE  = "distributed",
    parameter NORM_LATENCY = 1,
    // reorder fifo
    parameter FIFO_TYPE  = "block",
    parameter FIFO_LATENCY = 2
)
(
    input   clk,
    input   rst,
    
    input   i_vld,
    output  i_rdy,
    input   [N_INW * BIT_IN -1 : 0]    i_data,
    
    output  o_vld,
    input   o_rdy,
    output  [N_ACT * BIT_OT -1 : 0]    o_data
);

wire pwc_vld, pwc_rdy;
wire [N_INH * N_OUT * BIT_CV-1 : 0] pwc_data;

pwc2 #(
    .N_INH       ( N_INH        ),
    .N_INW       ( N_INW        ),
    .N_OUT       ( N_OUT        ),
    .N_ICH       ( N_ICH        ),
    .N_OCH       ( N_OCH        ),
    .BIT_I       ( BIT_IN       ),
    .BIT_W       ( BIT_WT       ),
    .BIT_O       ( BIT_CV       ),
    .PE_TYPE     ( PWCV_PE      ),
    .ROM_TYPE    ( PWET_TYPE    ),
    .ROM_FILE    ( PWET_FILE    ),
    .ROM_LATENCY ( PWET_LATENCY ),
    .RAM_TYPE    ( PBUF_TYPE     ),
    .RAM_LATENCY ( PBUF_LATENCY  )
) inst_pwc2
(
    .clk        (clk),
    .rst        (rst),
    .i_vld      (i_vld),
    .i_rdy      (i_rdy),
    .i_data     (i_data),
    .o_vld      (pwc_vld),
    .o_rdy      (pwc_rdy),
    .o_data     (pwc_data)
);

wire rdu_vld, rdu_rdy;
wire [N_ACT * BIT_CV -1 : 0] rdu_data;

generate
    if (N_INH * N_OUT > N_ACT) begin
        reduce2 #(
            .N      ( N_INH * N_OUT / N_ACT ),
            .WIDTH  ( N_ACT * BIT_CV        )
        ) inst_reduce (
            .clk                     ( clk      ),
            .rst                     ( rst      ),

            .i_rdy                   ( pwc_rdy  ),
            .i_vld                   ( pwc_vld  ),
            .i_data                  ( pwc_data ),

            .o_rdy                   ( rdu_rdy  ),
            .o_vld                   ( rdu_vld  ),
            .o_data                  ( rdu_data )
        );
    end
    else begin
        assign pwc_rdy = rdu_rdy;
        assign rdu_vld = pwc_vld;
        assign rdu_data = pwc_data;
    end
endgenerate

wire skd_vld, skd_rdy;
wire [N_ACT * BIT_CV -1 : 0] skd_data;

hs_fifo2 #(
    .WIDTH  ( N_ACT * BIT_CV    )
) inst_skid (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( rdu_rdy  ),
    .i_vld                   ( rdu_vld  ),
    .i_data                  ( rdu_data ),

    .o_rdy                   ( skd_rdy  ),
    .o_vld                   ( skd_vld  ),
    .o_data                  ( skd_data )
);

wire act_vld, act_rdy;
wire [N_ACT * BIT_OT -1 : 0] act_data;

norm_actv #(
    .N_IO       (N_ACT),
    .N_CH       (N_OCH),
    .N_BK       (N_OUT / N_ACT),
    .N_RP       (N_INH),
    .BIT_I      (BIT_CV),
    .BIT_B      (BIT_NB),
    .BIT_M      (BIT_NM),
    .BIT_O      (BIT_OT),
    .RSHIFT     (16),
    .PE_TYPE    (NORM_PE),
    .ROM_FILE   (NORM_FILE),
    .ROM_TYPE   (NORM_TYPE),
    .ROM_LATENCY(NORM_LATENCY)
) inst_norm_actv
(
    .clk        (clk),
    .rst        (rst),
    .i_vld      (skd_vld),
    .i_rdy      (skd_rdy),
    .i_data     (skd_data),
    .o_vld      (act_vld),
    .o_rdy      (act_rdy),
    .o_data     (act_data)
);

generate
    if (N_INH > 1) begin
        pwc2_reorder #(
            .N_IO        ( N_ACT   ),
            .N_HI        ( N_INH   ),
            .N_BK        ( N_OUT   ),
            .N_CH        ( N_OCH   ),
            .BIT         ( BIT_OT  ),
            .RAM_TYPE    ( FIFO_TYPE ),
            .RAM_LATENCY ( FIFO_LATENCY )
        )
        inst_reorder (
            .clk                     ( clk      ),
            .rst                     ( rst      ),

            .i_rdy                   ( act_rdy  ),
            .i_vld                   ( act_vld  ),
            .i_data                  ( act_data ),

            .o_rdy                   ( o_rdy    ),
            .o_vld                   ( o_vld    ),
            .o_data                  ( o_data   )
        );
    end
    else begin
        assign act_rdy = o_rdy;
        assign o_vld = act_vld;
        assign o_data = act_data;
    end
endgenerate

endmodule
